/*
 * $Id: UnPackNCopyTask.java,v 1.4 2009/10/09 20:30:54 oliver Exp $
 * 
 * Copyright (C) 2006 General Electric Company. All Rights Reserved.
 * 
 * This software is the confidential and proprietary information of the General
 * Electric Company (GE). You shall not disclose this software and shall use it
 * only in accordance with the terms of the license agreement you entered into
 * with GE.
 * 
 * GE MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
 * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
 * NON-INFRINGEMENT. GE SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
 * AS A RESULT OF USING, MODIFYING, OR DISTRIBUTING THIS SOFTWARE OR ITS
 * DERIVATIVES.
 */
package com.ge.healthcare.autosc.processor.dispatcher.tasks;

import java.io.File;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

import org.apache.regexp.RE;
import org.apache.regexp.RESyntaxException;

import com.ge.healthcare.autosc.common.ASCLogger;
import com.ge.healthcare.autosc.common.ASCUtil;
import com.ge.healthcare.autosc.common.database.pojo.ActFileTransferDetails;
import com.ge.healthcare.autosc.common.database.pojo.ActTask;
import com.ge.healthcare.autosc.common.database.pojo.CfgTaskFtDetails;
import com.ge.healthcare.autosc.common.dbLogger.StepLogger;
import com.ge.healthcare.autosc.common.util.tar.AscTar;
import com.ge.healthcare.autosc.processor.exception.FileTransferException;

public class UnPackNCopyTask extends FileCopyTask {

	private boolean allFilesCopied;

	public void executeTask(ActTask task) {
		String methodName = "executeTask";
		ASCLogger.debug(this.getClass(), methodName,
				"Received file transfer task for:" + task);
		try {
			this.setUp(task);
			transferFiles(this.taskFTDetailsLst, this.ftDetailsLst);
		} finally {
			this.complete();
		}
		ASCLogger.debug(this.getClass(), methodName,
				"Completed file transfer task for:" + task);
	}

	/**
	 * Does the following: <br>
	 * 1. Loops through the file name patterns as defined<br>
	 * 2. Loops though all the files and transfers the same<br>
	 * 
	 * Any files that are tar or ziped will be unzipped and placed
	 * 
	 * @param ftDetailsLst2
	 * @param taskFTDetailsLst
	 */
	protected void transferFiles(List<CfgTaskFtDetails> taskFTDetailsLst,
			List<ActFileTransferDetails> ftDetailsLst) {
		for (CfgTaskFtDetails ftDetail : taskFTDetailsLst) {
			try {
				RE reFilePattern = new RE(ftDetail.getId().getLogFilePattern()
						.trim());
				for (ActFileTransferDetails fileInfo : ftDetailsLst) {
					transferAndUnpack(ftDetail, reFilePattern, fileInfo);
				}
			} catch (RESyntaxException reSyntaxException) {
				throw new FileTransferException(
						"Invalid regular expression syntax defined for file name pattern:"
								+ ftDetail, PRC_FT_F_REX_001);
			}
		}
	}

	/**
	 * Transfer and unpack a single file
	 * 
	 * @param ftDetail
	 * @param reFilePattern
	 * @param fileInfo
	 */
	private void transferAndUnpack(CfgTaskFtDetails ftDetail, RE reFilePattern,
			ActFileTransferDetails fileInfo) {
		allFilesCopied = true;
		try {
			// Unpack Dir should be jobdirs + task + file transfer
			// id
			StringBuffer unPackDir = new StringBuffer(fileInfo.getFileDir());
			unPackDir.append("/").append(task.getTaskId());
			unPackDir.append("/").append(fileInfo.getFtId());
			List<ActFileTransferDetails> fileList = unPackFile(fileInfo,
					unPackDir.toString());
			for (ActFileTransferDetails file : fileList) {
				transferFile(ftDetail, file, reFilePattern);
			}
			updateStatus(fileInfo.getFtId(), task.getTaskId(), fileInfo
					.getFileName());
		} catch (RuntimeException re) {
			StringBuffer msg = new StringBuffer("File Transfer Failure: ");
			msg.append(re.getMessage());
			ASCLogger.error(this.getClass(), "transferFiles", msg.toString(),
					re);
			StepLogger.addStepLog(msg.toString());
			this.hasFailed = true;
		}
	}

	/**
	 * Update the status of a single file
	 * 
	 * @param ftId
	 * @param taskId
	 * @param fileName
	 */
	private void updateStatus(Long ftId, Long taskId, String fileName) {
		if (allFilesCopied) {
			ftRecordDAO.saveSuccess(ftId, taskId, fileName);
		} else {
			ftRecordDAO.saveFailure(ftId, taskId, fileName);
		}
	}

	/**
	 * Unpack a single file and return a list of the files that been unpacked
	 * 
	 * @param fileInfo
	 * @param unPackDir
	 * @return
	 */
	public List<ActFileTransferDetails> unPackFile(
			ActFileTransferDetails fileInfo, String unPackDir) {
		boolean gzipOnly = false;
		StringBuffer msg = new StringBuffer("Unpacking File: ");
		List<ActFileTransferDetails> fileList = new ArrayList<ActFileTransferDetails>();
		try {
			msg.append(fileInfo).append(" to ").append(unPackDir);
			String extension = fileInfo.fileExt();
			File tempDir = new File(unPackDir);
			int fileType = -1;
			if (".tar.gz".equalsIgnoreCase(extension)
					|| ".tgz".equalsIgnoreCase(extension)) {
				fileType = AscTar.GZIP_TAR;
				msg.append(" File is Tar GZip ");
			} else if (".gz".equalsIgnoreCase(extension)) {
				fileType = AscTar.GZIP_ONLY;
				msg.append(" File is GZip Only ");
				// have to create the directory for gzip
				gzipOnly = true;
				tempDir.mkdirs();
			} else if (".tar".equalsIgnoreCase(extension)) {
				fileType = AscTar.TAR_ONLY;
				msg.append(" File is Tar Only ");
			} else {
				msg.append("File Type Default ");
				fileList.add(fileInfo);
				ASCLogger.debug(this.getClass(), "unPackFile", msg.toString());
				StepLogger.addStepLog(msg.toString());
				// Returning Now
				return fileList;
			}
			AscTar tar = new AscTar(fileInfo.getFile(), fileType);
			Enumeration<String> extractedFiles = tar.extract(tempDir);
			if ((extractedFiles == null)
					|| (extractedFiles.hasMoreElements() == false)) {
				msg.append("Extraction Error: ").append(
						tar.getLastStatusMessage());
			} else {
				while ((extractedFiles != null)
						&& (extractedFiles.hasMoreElements())) {
					StringBuffer fileMsg = new StringBuffer("Extract File: ");
					String exFile = extractedFiles.nextElement().toString();
					fileMsg.append(exFile);
					File extractFile = null;
					if (gzipOnly) {
						extractFile = new File(exFile);
					} else {
						extractFile = new File(unPackDir, exFile);
					}
					if (extractFile.isDirectory()) {
						fileMsg.append(" - Internal Directory Skipping");
						StepLogger.addStepLog(fileMsg.toString());
					} else {
						// Using file in the event the file itself is under a
						// directory
						fileList.add(new ActFileTransferDetails(fileInfo,
								extractFile.getParentFile().getAbsolutePath(),
								extractFile.getName()));
					}
					ASCLogger.debug(this.getClass(), "unPackFile", fileMsg
							.toString());
					StepLogger.addStepLog(fileMsg.toString());
				}
			}
		} catch (RuntimeException re) {
			msg.append("Runtime Exception: ").append(re.getMessage());
			ASCLogger.error(this.getClass(), "unPackFile", msg.toString());
		} finally {
			ASCLogger.info(this.getClass(), "unPackFile", msg.toString());
			StepLogger.addStepLog(msg.toString());
		}
		return fileList;
	}

	/**
	 * Transfer a single file to the destination
	 * 
	 * @param ftDetail
	 * @param fileInfo
	 * @param reFilePattern
	 */
	public void transferFile(CfgTaskFtDetails ftDetail,
			ActFileTransferDetails fileInfo, RE reFilePattern) {
		String methodName = "transferFile";
		if (ASCUtil.isNullOrEmpty(fileInfo.getFileName())) {
			StringBuffer msg = new StringBuffer().append(
					"Null File name found for file details:").append(fileInfo);
			msg.append(" while executing task:").append(task);
			ASCLogger.error(this.getClass(), methodName, msg.toString(), true);
		} else if (reFilePattern.match(fileInfo.getFileName().trim())) {
			this.performTransfer(ftDetail, fileInfo);
		} else {
			StringBuffer msg = new StringBuffer().append("Ignoring file:");
			msg.append(fileInfo.getFileName());
			msg.append(" as it failed to match pattern:");
			msg.append(ftDetail.getId().getLogFilePattern());
			ASCLogger.error(this.getClass(), methodName, msg.toString(), true);
		}
	}

	/**
	 * Recording of copy status should only occur once per file that has
	 * transferred. So overloading global methods to allow recording
	 */
	protected void markFailed(Long ftId, String fileUrl, String message,
			Throwable throwable) {
		String methodName = "markFailed";
		hasFailed = true;
		allFilesCopied = false;
		ASCLogger.error(this.getClass(), methodName, message, throwable, true);
	}

	/**
	 * Recording of copy status should only occur once per file that has
	 * transferred. So overloading global methods to allow recording
	 * 
	 * @param ftId
	 * @param destFile
	 */
	protected void markSuccess(Long ftId, File destFile) {
	}
}
